import { Compressor } from './types'

const compressors: { [extName: string]: Compressor } = Object.create(null)

const defaultCompressor: Compressor = source => source

const safeRequire = (id: string) => {
  try {
    return require(id)
  } catch {}
}

// js
;(function () {
  let compressor: Compressor | undefined

  //@ts-ignore
  let esbuild: typeof import('esbuild')
  //@ts-ignore
  let terser: typeof import('terser')

  if ((terser = safeRequire('terser') ?? safeRequire('uglify-js')) && typeof terser.minify === 'function') {
    //@ts-ignore
    const opts: import('terser').MinifyOptions = {
      compress: {
        keep_classnames: true,
        keep_fnames: true
      },
      mangle: false
    }

    compressor = async source => {
      return (await terser.minify(source.toString(), opts)).code || source
    }
  } else if ((esbuild = safeRequire('esbuild')) && typeof esbuild.transform === 'function') {
    //@ts-ignore
    const opts: import('esbuild').TransformOptions = {
      loader: 'jsx',
      minifyIdentifiers: false,
      minifySyntax: true,
      minifyWhitespace: true,
      jsx: 'preserve'
    }

    compressor = async source => {
      return (await esbuild.transform(source.toString(), opts)).code
    }
  }

  if (compressor) {
    compressors['js'] = compressors['jsx'] = compressors['mjs'] = compressors['cjs'] = compressor
  }
})()

//css
;(function () {
  let compressor: Compressor | undefined

  //@ts-ignore
  let esbuild: typeof import('esbuild')
  //@ts-ignore
  let CleanCSS: typeof import('clean-css')
  //@ts-ignore
  let csso: typeof import('csso')

  if (typeof (CleanCSS = safeRequire('clean-css')) === 'function') {
    compressor = async source => {
      return new CleanCSS().minify(source.toString()).styles
    }
  } else if ((csso = safeRequire('csso')) && typeof csso.minify) {
    compressor = async source => {
      return csso.minify(source.toString()).css
    }
  } else if ((esbuild = safeRequire('esbuild')) && typeof esbuild.transform === 'function') {
    //@ts-ignore
    const opts: import('esbuild').TransformOptions = {
      loader: 'css',
      minify: true,
      minifyIdentifiers: false,
      minifySyntax: true,
      minifyWhitespace: true
    }

    compressor = async source => {
      return (await esbuild.transform(source.toString(), opts)).code
    }
  }

  if (compressor) {
    compressors['css'] = compressor
  }
})()

//html
;(function () {
  let compressor: Compressor | undefined

  //@ts-ignore
  let htmlMinify: typeof import('html-minifier-terser')

  if ((htmlMinify = safeRequire('html-minifier-terser')) && typeof htmlMinify.minify === 'function') {
    compressor = source => {
      return htmlMinify.minify(source.toString())
    }
  }

  if (compressor) {
    compressors['htm'] = compressors['html'] = compressor
  }
})()

//json
;(function () {
  //@ts-ignore
  const _JSON: typeof JSON = safeRequire('json5') ?? JSON

  compressors['json'] = source => {
    return JSON.stringify(_JSON.parse(source.toString()))
  }
})()

//images
;(function () {
  //svg
  //@ts-ignore
  const svgo: typeof import('svgo') = safeRequire('svgo')
  if (svgo && typeof svgo.optimize === 'function' && typeof svgo.loadConfig === 'function') {
    svgo.loadConfig()
    compressors['svg'] = compressors['svgz'] = source => {
      return svgo.optimize(source.toString()).data
    }
  }
})()

const compressorsProxy = new Proxy(compressors, {
  get: (target, p) => {
    return target[p as string] ?? defaultCompressor
  }
})

export { compressorsProxy as compressors }
